
#include <BPainter>
#include <BMouseEvent>
#include <BCursor>
#include <BApplication>
#include <BSystem>
#include <BScreen>

#include <BDialog>
#include "member_BWidget.h"
#include "member_BDialog.h"
#include "member_BEvent.h"
#include "member_BStyle.h"
#include "IGlobal.h"

using namespace BWE;

#define member					(*(member_BDialog*)_ptr)
#define member_allocate()		_ptr = new member_BDialog(this)
#define member_release()		delete (member_BDialog*)_ptr

BDialog::BDialog(BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setBorder(5);
	this->hide();
	this->addTitleButton(Button_Close);
}
BDialog::BDialog(const BString& title, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setTitle(title);
	this->setBorder(5);
	this->hide();
	this->addTitleButton(Button_Close);
}
BDialog::~BDialog()
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BObject* object = member.buttonLayout->object(i);
		member.disconnect(object, Signal_Clicked);
	}
	member_release();
}

bool BDialog::setTitle(const BString& title)
{
	if (BWidget::setTitle(title))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
bool BDialog::setIcon(BIcon* icon)
{
	if (BWidget::setIcon(icon))
	{
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
void BDialog::setFrameless(bool frameless)
{
	BWidget::setFrameless(frameless);
	member.dirty = true;
}

bool BDialog::setIcon(StyleIcon iconName)
{
	BIcon* icon = BSystem::TypedIcon(iconName);
	return setIcon(icon);
}

void BDialog::setTitleVisible(bool titleVisible)
{
	if (member.titleVisible != titleVisible)
	{
		member.titleVisible = titleVisible;
		member.dirty = true;
		this->fresh();
	}
}
bool BDialog::titleVisible() const
{
	return member.titleVisible;
}

void BDialog::setTitleAlign(Align titleAlign)
{
	if (member.alignment.ia_align != titleAlign)
	{
		member.alignment.ia_align = titleAlign;
		member.dirty = true;
		this->fresh();
	}
}
Align BDialog::titleAlign() const
{
	return member.alignment.ia_align;
}

void BDialog::setTitleForm(Form titleForm)
{
	if (member.alignment.ia_form != titleForm)
	{
		member.alignment.ia_form = titleForm;
		member.dirty = true;
		this->emit(Signal_FormChanged, titleForm);
		this->fresh();
	}
}
Form BDialog::titleForm() const
{
	return member.alignment.ia_form;
}

void BDialog::setTitleButtonAlign(Align titleButtonAlign)
{
	if (member.buttonAlign)
	{
		member.buttonAlign = titleButtonAlign;
		member.dirty = true;
		this->fresh();
	}
}
Align BDialog::titleButtonAlign() const
{
	return member.buttonAlign;
}

BButton* BDialog::addTitleButton(Button button)
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* button = member.buttonLayout->widget(i);
		if (button->asset() == button)
			return (BButton*)button;
	}

	BButton* btn = new BButton(this);
	if (button == Button_Maximize)
		btn->setCheckable(true);
	btn->setAsset(button);

	if (Graph graph = member.ButtonGraph(button))
	{
		btn->setGraph(graph);
		btn->setForm(Form_IconAlone);
	}
	if (const char* name = member.ButtonName(button))
	{
		btn->setName(name);
	}
	member.buttonLayout->addWidget(btn);
	member.connect(btn, Signal_Clicked, &member_BDialog::slotButtonClicked);
	return btn;
}
bool BDialog::addTitleButton(BButton* button)
{
	if (!button)
		return false;
	if (!member.buttonLayout->contain(button))
	{
		this->addChild(button);
		member.buttonLayout->addWidget(button);
		member.connect(button, Signal_Clicked, &member_BDialog::slotButtonClicked);
	}
	return true;
}

BButton* BDialog::insertTitleButton(int pos, Button button)
{
	if (pos < 0 || pos > member.buttonLayout->count())
		return 0;
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* button = member.buttonLayout->widget(i);
		if (button->asset() == button)
			return (BButton*)button;
	}

	BButton* btn = new BButton(this);
	if (button == Button_Maximize)
		btn->setCheckable(true);
	btn->setAsset(button);

	if (Graph graph = member.ButtonGraph(button))
	{
		btn->setGraph(graph);
		btn->setForm(Form_IconAlone);
	}
	if (const char* name = member.ButtonName(button))
	{
		btn->setName(name);
	}
	member.buttonLayout->insertWidget(pos, btn);
	member.connect(btn, Signal_Clicked, &member_BDialog::slotButtonClicked);
	return btn;
}
bool BDialog::insertTitleButton(int pos, BButton* button)
{
	if (!button)
		return false;
	if (pos < 0 || pos > member.buttonLayout->count())
		return 0;
	if (!member.buttonLayout->contain(button))
	{
		if (button->asset() == Button_Maximize)
			button->setCheckable(true);
		this->addChild(button);
		member.buttonLayout->insertWidget(pos, button);
		member.connect(button, Signal_Clicked, &member_BDialog::slotButtonClicked);
	}
	return true;
}

bool BDialog::removeTitleButton(Button button)
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* widget = member.buttonLayout->widget(i);
		if (widget->asset() == button)
		{
			member.disconnect(widget);
			this->removeChild(widget);
			member.buttonLayout->remove(widget);
			member.dirty = true;
			this->fresh();
			return true;
		}
	}
	return false;
}
bool BDialog::removeTitleButton(BButton* button)
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* widget = member.buttonLayout->widget(i);
		if (widget == button)
		{
			member.disconnect(widget);
			this->removeChild(widget);
			member.buttonLayout->remove(widget);
			member.dirty = true;
			this->fresh();
			return true;
		}
	}
	return false;
}
void BDialog::clearTitleButtons()
{
	if (member.buttonLayout->count())
	{
		for (int i = 0; i < member.buttonLayout->count(); i++)
		{
			BWidget* widget = member.buttonLayout->widget(i);
			member.disconnect(widget);
			this->removeChild(widget);
		}
		member.buttonLayout->clear();
		member.dirty = true;
		this->fresh();
	}
}

int BDialog::titleButtonCount() const
{
	return member.buttonLayout->count();
}
BButton* BDialog::titleButton(int index)
{
	if (BWidget* widget = member.buttonLayout->widget(index))
		return dynamic_cast<BButton*>(widget);
	return 0;
}
const BButton* BDialog::titleButton(int index) const
{
	if (BWidget* widget = member.buttonLayout->widget(index))
		return dynamic_cast<BButton*>(widget);
	return 0;
}

BButton* BDialog::titleButton(Button button)
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* widget = member.buttonLayout->widget(i);
		if (widget->asset() == button)
		{
			return dynamic_cast<BButton*>(widget);
		}
	}
	return 0;
}
const BButton* BDialog::titleButton(Button button) const
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* widget = member.buttonLayout->widget(i);
		if (widget->asset() == button)
		{
			return dynamic_cast<BButton*>(widget);
		}
	}
	return 0;
}

BButton* BDialog::titleButton(const BString& name)
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* widget = member.buttonLayout->widget(i);
		if (widget->name() == name)
		{
			return dynamic_cast<BButton*>(widget);
		}
	}
	return 0;
}
const BButton* BDialog::titleButton(const BString& name) const
{
	for (int i = 0; i < member.buttonLayout->count(); i++)
	{
		BWidget* widget = member.buttonLayout->widget(i);
		if (widget->name() == name)
		{
			return dynamic_cast<BButton*>(widget);
		}
	}
	return 0;
}

bool BDialog::show(ShowState state)
{
	if (member.showState != state)
	{
		if (IWindow* window = widget_member(this)->window)
		{
			if (state == Show_Normal)
				window->showNormal();
			else if (state == Show_Minimized)
				window->showMinimized();
			else if (state == Show_Maximized)
				window->showMaximized();
			else if (state == Show_FullScreen)
				window->showFullScreen();
			else
				return false;
		}
		else if(BWidget* parent = member.parent)
		{
			if (state == Show_Normal)
			{
				if (member.normalRect.valid())
				{
					if (BLayout* centerLayout = this->layout(Part_Center))
						centerLayout->setVisible(true);
					if (BLayout* bottomLayout = this->layout(Part_Bottom))
						bottomLayout->setVisible(true);
					if (member.showState == Show_Minimized)
						this->setSize(member.normalRect.size());
					if (member.showState == Show_Maximized)
						this->setRect(member.normalRect);
					this->setMargin(member.normalMargin);
					member.normalRect.reset();
				}
			}
			else if (state == Show_Minimized)
			{
				member.normalRect = this->rect();
				member.normalMargin = this->margin();
				if (BLayout* centerLayout = this->layout(Part_Center))
					centerLayout->setVisible(false);
				if (BLayout* bottomLayout = this->layout(Part_Bottom))
					bottomLayout->setVisible(false);
				BMargin margin = this->margin();
				BSize csize = this->centerSize();
				this->setMargin(margin.left(), 0, margin.right(), 0);
				this->setCenterSize(csize.width(), 0);
			}
			else if (state == Show_Maximized)
			{
				member.normalRect = this->rect();
				BWidget* root = this->root();
				if (root && widget_member(root)->window && widget_member(root)->window->popupWidgets.contain(this))
				{
					BRect maxRect = root->centerRect();
					this->setAbsoluteRect(maxRect);
				}
				else
				{
					BRect maxRect = parent->centerRect();
					if (this->rect() != maxRect)
						this->setRect(maxRect);
				}
			}
			else
			{
				BWidget::show();
			}
		}
		member.showState = state;
		for (int i = 0; i < member.buttonLayout->count(); i++)
		{
			if (member.buttonLayout->object(i)->asset() == Button_Maximize)
			{
				BButton* button = (BButton*)member.buttonLayout->object(i);
				button->setChecked(state == Show_Maximized);
			}
		}
		return true;
	}
	BWidget::show();
	return true;
}
ShowState BDialog::showState() const
{
	return member.showState;
}

void BDialog::close()
{
	BEvent closeEvent(Event_Close);
	closeEvent.setValue(member.result);
	widget_member(this)->procEvent(closeEvent);
}
void BDialog::close(const BValue& result)
{
	if (member.result != result)
	{
		member.result = result;
		this->emit(Signal_ResultChanged, result);
	}
	BEvent closeEvent(Event_Close);
	closeEvent.setValue(member.result);
	widget_member(this)->procEvent(closeEvent);
}

void BDialog::setResult(const BValue& result)
{
	if (member.result != result)
	{
		member.result = result;
		this->emit(Signal_ResultChanged, result);
	}
}
const BValue& BDialog::result() const
{
	return member.result;
}

const BValue& BDialog::execute()
{
	this->popup();
	while (!app_done && this->visible())
	{
		App_CycleBegin();
		if (this->modal() && widget_member(this)->window)
			App_Event(widget_member(this)->window);
		else
			App_Event();
		App_Signal();
		App_Update();
		App_Frame();
		App_CycleEnd();
	}
	return member.result;
}

BSize BDialog::sizeHint() const
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	int titleWidth = member.alignment.ia_hintSize.width();
	int btnsWidth = member.buttonLayout->sizeHint().width();
	return BWidget::preferSize(titleWidth + btnsWidth, 0);
}

bool BDialog::input(const BString& text, const BValue& value)
{
	if (text == "result")
	{
		member.result = value;
		return true;
	}
	if (text == "centering")
	{
		member.centering = value;
		return true;
	}
	if (text == "adapting")
	{
		member.adapting = value;
		return true;
	}
	return BWidget::input(text, value);
}
bool BDialog::query(const BString& text, BValue& value) const
{
	if (text == "result")
	{
		value = member.result;
		return true;
	}
	if (text == "text-rect")
	{
		value = member.alignment.ia_textRect;
		return true;
	}
	if (text == "text-area")
	{
		value = member.alignment.ia_textArea;
		return true;
	}
	if (text == "icon-rect")
	{
		value = member.alignment.ia_iconRect;
		return true;
	}
	if (text == "icon-area")
	{
		value = member.alignment.ia_iconArea;
		return true;
	}
	if (text == "centering")
	{
		value = member.centering;
		return true;
	}
	if (text == "adapting")
	{
		value = member.adapting;
		return true;
	}
	return BWidget::query(text, value);
}

void BDialog::event(const BEvent& event)
{
	if (event.type() == Event_Move)
	{
		member.centering = false;
	}
	BWidget::event(event);
}

void BDialog::popupEvent(const BEvent& event)
{
	if (event.value())
	{
		member.result.clear();
		if (!widget_member(this)->visible)
		{
			member.adapting = true;
			member.centering = true;
		}
		if (this->parent() && widget_member(this)->upperLayout == 0)
		{
			const BPoint& abPos = this->absolutePos();
			if (abPos.y() < 0)
				this->move(0, -abPos.y());
		}
	}
	BWidget::popupEvent(event);
}
void BDialog::closeEvent(const BEvent& event)
{
	this->post(Signal_Close, member.result);
	BWidget::closeEvent(event);
}

void BDialog::activeEvent(const BEvent& event)
{
	BWidget::activeEvent(event);
	if (event.value() && member.showState == Show_Minimized)
	{
		if (IWindow* window = widget_member(this)->window)
		{
			if (window->isMaximized())
				member.showState = Show_Maximized;
			else if (window->isFullScreen())
				member.showState = Show_FullScreen;
			else
				member.showState = Show_Normal;
		}
		else if (BWidget* parent = member.parent)
		{
			BRect maxRect = parent->centerRect();
			if (this->rect() == maxRect)
				member.showState = Show_Maximized;
			else
				member.showState = Show_Normal;
		}
	}
}
void BDialog::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BWidget::resizeEvent(event);
}

void BDialog::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	const BStyle* style = event.value();
	member.titleStyle = 0;

	style_member(style)->assign(Value_Align, member.alignment.ia_align);
	style_member(style)->assign(Value_Spacing, member.alignment.ia_spacing);
	style_member(style)->assign(Value_Icon_Size, member.titleIconSize);

	member.autoTop = style->value(Value_Perch_Top).empty();

	if (const BStyle* titleStyle = style->annex("title"))
	{
		member.titleStyle = titleStyle;
		member.titleHeight = -1;

		style_member(titleStyle)->assign(Value_Height, member.titleHeight);
		style_member(titleStyle)->assign(Value_Icon_Size, member.titleIconSize);
		style_member(titleStyle)->assign(Value_Align, member.alignment.ia_align);
		style_member(titleStyle)->assign(Value_Spacing, member.alignment.ia_spacing);
		style_member(titleStyle)->assign(Value_Margin_Left, member.titleMargin.left());
		style_member(titleStyle)->assign(Value_Margin_Top, member.titleMargin.top());
		style_member(titleStyle)->assign(Value_Margin_Right, member.titleMargin.right());
		style_member(titleStyle)->assign(Value_Margin_Bottom, member.titleMargin.bottom());
	}

	member.centering = true;
	member.adapting = true;
	member.dirty = true;
}
void BDialog::freshEvent(const BEvent& event)
{
	BWidget::freshEvent(event);
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	if (member.adapting)
	{
		member.adaptSize();
		member.adapting = false;
	}
	if (member.centering)
	{
		member.centerAlign();
		member.centering = false;
	}
}
void BDialog::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawPerch();
	painter.drawTitle();

	if (member.titleVisible)
	{
		if (widget_member(this)->pixmap || widget_member(this)->parent || widget_member(this)->frameless)
		{
			painter.setStyle(member.titleStyle);
			State state = painter.state();
			if (this->hovered())
			{
				if (member.pressed)
					state |= State_Pressed;
				else
					state |= State_Hovered;
			}
			if (this->title().size())
			{
				painter.setColor(Color_Text, state);
				if (widget_member(this)->t_title.size())
					painter.drawText(member.alignment.ia_textRect, widget_member(this)->t_title);
				else
					painter.drawText(member.alignment.ia_textRect, widget_member(this)->title);
			}
			if (BIcon* icon = this->icon())
			{
				if (BImage* image = icon->image(state))
				{
					painter.drawImage(member.alignment.ia_iconRect, image);
				}
			}
		}
		painter.setStyle(0);
	}

	painter.drawMargin();
	painter.drawBorder();
}

void BDialog::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	member.hoverTitle = false;
	member.pressed = false;
	member.resizePart = Part_None;
	BWidget::mouseEnterEvent(mouseEvent);
}
void BDialog::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	member.hoverTitle = false;
	member.pressed = false;
	BWidget::mouseLeaveEvent(mouseEvent);
}
void BDialog::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (member.resizable())
	{
		BPoint pos = mouseEvent.pos();
		Button button = mouseEvent.button();
		if (mouseEvent.button() == Button_Left && member.resizePart)
		{
			member.pressed = true;
			member.pressedPos = mouseEvent.globalPos();
			member.pressedRect = this->rect();
			this->fresh();
		}
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BDialog::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (member.resizable())
	{
		member.pressed = false;
		member.pressedPos.reset();
		member.pressedRect.reset();
		this->fresh();
	}
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BDialog::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	if (member.resizable())
	{
		BPoint pos = mouseEvent.pos();
		BPoint gpos = mouseEvent.globalPos();
		if (member.showState != Show_Maximized)
		{
			if (member.pressed && member.resizePart && gpos != member.pressedPos)
			{
				BPoint offset = gpos - member.pressedPos;
				BRect rect = member.pressedRect;
				if (member.resizePart == Part_All)
				{
					if (member.showState == Show_Maximized)
					{
						float xratio = float(pos.x() - member.pressedRect.x()) / member.pressedRect.width();
						float yratio = float(pos.y() - member.pressedRect.y()) / member.pressedRect.height();
						float posx = member.normalRect.x() + member.normalRect.width() * xratio;
						float posy = member.normalRect.y() + member.normalRect.height() * yratio;
						member.pressedPos = this->globalPos() + BPoint((int)posx, (int)posy);
						member.pressedRect = member.normalRect;
						show(Show_Normal);
					}
					else
					{
						rect.move(offset);
						this->setRect(rect);
					}
				}
				else
				{
					if (member.resizePart & Part_Top)
						rect.top() = rect.top() + offset.y();
					if (member.resizePart & Part_Left)
						rect.left() = rect.left() + offset.x();
					if (member.resizePart & Part_Right)
						rect.right() = rect.right() + offset.x();
					if (member.resizePart & Part_Bottom)
						rect.bottom() = rect.bottom() + offset.y();
					BSize size = this->sizeLimit(rect.size());
					if (this->setSize(size))
						this->setPos(rect.pos());
				}
				return;
			}
		}

		CursorType type = Cursor_None;
		bool hoverTitle = member.titleRect.contain(pos);
		if (member.hoverTitle != hoverTitle)
		{
			member.hoverTitle = hoverTitle;
			this->fresh();
		}

		member.resizePart = Part_None;
		if (member.hoverTitle)
		{
			member.resizePart = Part_All;
		}
		if (member.showState != Show_Maximized)
		{
			if (member.cornerLeftTop.contain(pos))
			{
				member.resizePart = Part_LeftTop;
				type = Cursor_SizeLeftTop;
			}
			else if (member.cornerLeftBottom.contain(pos))
			{
				member.resizePart = Part_LeftBottom;
				type = Cursor_SizeLeftBottom;
			}
			else if (member.cornerRightTop.contain(pos))
			{
				member.resizePart = Part_RightTop;
				type = Cursor_SizeRightTop;
			}
			else if (member.cornerRightBottom.contain(pos))
			{
				member.resizePart = Part_RightBottom;
				type = Cursor_SizeRightBottom;
			}
			else if (member.borderTop.contain(pos))
			{
				member.resizePart = Part_Top;
				type = Cursor_SizeVer;
			}
			else if (member.borderLeft.contain(pos))
			{
				member.resizePart = Part_Left;
				type = Cursor_SizeHor;
			}
			else if (member.borderRight.contain(pos))
			{
				member.resizePart = Part_Right;
				type = Cursor_SizeHor;
			}
			else if (member.borderBottom.contain(pos))
			{
				member.resizePart = Part_Bottom;
				type = Cursor_SizeVer;
			}
			if (!this->cursor() || this->cursor()->type() != type)
			{
				this->setCursor(type);
			}
		}
	}
	BWidget::mouseMoveEvent(mouseEvent);
}

void BDialog::mouseWheelEvent(const BMouseEvent& mouseEvent)
{
	mouseEvent.accept();
	BWidget::mouseWheelEvent(mouseEvent);
}
void BDialog::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	if (member.resizable() && member.resizePart == Part_All)
	{
		BPoint pos = mouseEvent.pos();
		Button button = mouseEvent.button();
		if (button == Button_Left && member.titleRect.contain(pos))
		{
			if (member.showState == Show_Maximized)
				show(Show_Normal);
			else
				show(Show_Maximized);
			member.resizePart = Part_None;
			member.centering = false;
		}
	}
	BWidget::mouseDoubleClickEvent(mouseEvent);
}
